/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

 ****/
package org.activequant.calendar;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

import org.activequant.core.types.TimeStamp;
import org.activequant.core.util.Check;
import org.activequant.util.calendar.CalendarUtils;

/**
 * An implementation of a holiday calendar.
 * 
 * All date and time related elements are in the time zone of the holiday calendar. This implementation does not
 * guarantee an ordered list of holiday calendar entries. This class is immutable.<br>
 * HolidayCalendar extends ArrayList&gt;HolidayCalendarEntry&gt; implements IHolidayCalendar. Holds the following associated variables:
 * <ul>
 * <li>timeZone(TimeZone)</li>
 * </ul>
 * <b>History:</b><br>
 * - [Jun 14, 2008] Created (mark)<br>
 * - [Aug 15, 2008] refactored AbstractHolidayCalendar to HolidayCalendar and made it immutable (mark)<br>
 * 
 * @author mark
 */
public class HolidayCalendar extends ArrayList<HolidayCalendarEntry> implements IHolidayCalendar {

	private static final long serialVersionUID = 2602591412457330521L;
	/**
	 * private final TimeZone timeZone;
	 */
	private final TimeZone timeZone;

	/**
	 * Create a new holiday calendar given the holiday calendar settings.<br/>
	 * Constructs a HolidayCalendar (extends ArrayList&lt;HolidayCalendarEntry&gt; implements IHolidayCalendar) using the given settings(IHolidayCalendarSettings)
	 * to set its associated timeZone(TimeZone) and HolidayCalendarEntry elements.<br/>
	 * It also validates that the timeZone(TimeZone) of each HolidayCalendarEntry element matches the associated timeZone(TimeZone).
	 * @param settings
	 *            the holiday calendar settings to be used for this holiday calendar.
	 */
	public HolidayCalendar(final IHolidayCalendarSettings settings) {
		super();

		Check.notNull("settings", settings);

		this.timeZone = settings.getTimeZone();
		this.addAll(settings.getHolidays());

		checkTimeZoneOfEntries();
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and makes sure the timeZone(TimeZone) of each one
	 * matches the associated timeZone(TimeZone). Otherwise it throws an AssertionError with the "Different timezone not allowed on entry: ${entry}" message.
	 */
	private void checkTimeZoneOfEntries() {
		for (HolidayCalendarEntry entry : this) {
			if (!entry.getTimeZone().equals(this.timeZone)) {
				throw new AssertionError("Different timezone not allowed on entry: " + entry);
			}
		}

	}
	/**
	 * returns the associated timeZone(TimeZone)
	 */
	public TimeZone getTimeZone() {
		return this.timeZone;
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and returns whether any of them
	 * matches the day in the given when(TimeStamp)
	 */
	public boolean isOnSameDay(final TimeStamp when) {
		for (final HolidayCalendarEntry entry : this) {
			if (entry.isOnSameDay(when)) {
				return true;
			}
		}

		return false;
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and returns whether any of them
	 * is during the given when(TimeStamp)
	 */
	public boolean isHoliday(final TimeStamp when) {

		for (final HolidayCalendarEntry entry : this) {
			if (entry.isDuring(when)) {
				return true;
			}
		}

		return false;
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and returns whether any of them
	 * is during the given when(Calendar)
	 */
	public boolean isHoliday(final Calendar when) {
		return isHoliday(new TimeStamp(when.getTimeInMillis() * CalendarUtils.MILLIS_TO_NANOS));
	}
	/**
	 * this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) instance
	 */
	public List<HolidayCalendarEntry> getHolidays() {
		return this;
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and returns the one celebrated on the same day as the day/month 
	 * in given day(TimeStamp). If it can't find any, it returns null.
	 */
	public HolidayCalendarEntry getHoliday(final TimeStamp day) {
		for (final HolidayCalendarEntry entry : this) {
			if (entry.isOnSameDay(day)) {
				return entry;
			}
		}

		return null;
	}
	/**
	 * iterates the HolidayCalendarEntry elements in this HolidayCalendar (extends ArrayList&gt;HolidayCalendarEntry&gt;) and returns the one celebrated on the same day as the day/month 
	 * in given day(Calendar). If it can't find any, it returns null.
	 */
	public HolidayCalendarEntry getHoliday(Calendar day) {
		TimeStamp timeStamp = new TimeStamp(day.getTimeInMillis() * CalendarUtils.MILLIS_TO_NANOS);
		return getHoliday(timeStamp);
	}
}